
package domain;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
import java.util.Vector;

import domain.exceptions.CannotFlagSquareException;
import domain.exceptions.CannotPlaceAgentHereException;
import domain.exceptions.MultipleAgentsHaveTheSameLocation;
import domain.exceptions.TryingToRemoveNonExistingFlagException;

/**
 *
 * @author shaigi
 */
public class Board {

    private String          _fileName;
    private Square[][]      _board;
    private int             _numberOfRows;
    private int             _numberOfColumns;
    private Vector<Flag>    _flags;
    
    public Board(String fileName){
        this._fileName  = fileName;
        this._flags     = new Vector<Flag>();
        initializeBoard();
        parse();
    }
    
    public Board(Board other){
        // fileName:
    	this._fileName  = other._fileName;
        
    	// board:
    	this._board = new Square[other._numberOfRows][other._numberOfColumns];
    	this._flags = new Vector<Flag>();
    	for (int i = 0; i < other._numberOfRows; i++) {
    		for (int j = 0; j < other._numberOfColumns; j++) {
    			Square clonedSquare = null;
    			Square otherSquare = other.getSquare(i, j);
    			if (otherSquare instanceof SquareFree){
    				clonedSquare = new SquareFree((SquareFree)otherSquare);
    			} else if (otherSquare instanceof SquareIce){
    				clonedSquare = new SquareIce((SquareIce)otherSquare);
    			} else if (otherSquare instanceof SquareWall){
    				clonedSquare = new SquareWall((SquareWall)otherSquare);
    			}
    			this._board[i][j] = clonedSquare;

    			// flags:
    			if (this._board[i][j].isContainFlag()){
    				this._flags.addElement(this._board[i][j].getFlag());
    			}
//    			if ( other.getSquare(i, j).isContainFlag() ){
//    				try {
//    					Flag newFlag = new Flag(other.getSquare(i, j).getFlag());
//						this._board[i][j].setFlag(newFlag);
//						this._flags.addElement(newFlag);
//					} catch (CannotFlagSquareException e) {
//						e.printStackTrace();
//					}
//    			}
			}
		}
    	
    	// number of rows & columns:
    	this._numberOfRows = other._numberOfRows;
    	this._numberOfColumns = other._numberOfColumns;
    	
    	// flags:
//    	this._flags = new Vector<Flag>();
//    	for (Iterator iter = _flags.iterator(); iter.hasNext();) {
//			Flag flag = (Flag) iter.next();
//			this._flags.addElement(flag);
//		}
    }    
    
    public Square getSquare(int row, int column){
        return _board[row][column];
    }
    
    public void placeAgent(Agent agent, Square square) throws CannotPlaceAgentHereException{
        try{
        	int col = square.getColumn();
        	int row = square.getRow();
            _board[row][col].placeAgent(agent);
            agent.setSquare(square);
        } catch (CannotPlaceAgentHereException e){
            throw e;
        }
    }
    
    public void removeAgent(int x, int y) throws Exception{
        try{
            _board[x][y].removeAgent();
        } catch (Exception e){
            throw new Exception(e.getMessage());
        }
    }

    public Vector<Flag> getFlags() {
        return this._flags;
    }

    public void removeFlag(Square square) throws TryingToRemoveNonExistingFlagException {
    	// update relevant square:
    	square.removeFlag();

    	// updates flags list:
        boolean found = false;
        for (Iterator<Flag> it = _flags.iterator(); it.hasNext() && !found;) {
            Flag flag = it.next();
            if (flag.getSquare().equals(square)){
                _flags.remove(flag);
                found = true;
            }
        }
        if ( !found )
            throw new TryingToRemoveNonExistingFlagException();
    }

    private void initializeBoard() {
        int rowsTmp = 0;
        BufferedReader in = null;
        String str = "";
        try {
            in = new BufferedReader(new FileReader(_fileName));
        } catch (FileNotFoundException ex) {
            System.err.println("File " + _fileName + " not found");
            System.exit(4);
        }
        try {
            while ((str = in.readLine()) != null) {
                rowsTmp++;
            }
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
        }
        this._numberOfRows    = rowsTmp;
        try {
            in = new BufferedReader(new FileReader(_fileName));
            str = in.readLine();
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
        }
        this._numberOfColumns = str.length();
        this._board = new Square[_numberOfRows][_numberOfColumns];
    }

    private void parse() {
        int row    = 0;
        try {
            BufferedReader in = new BufferedReader(new FileReader(_fileName));
            String str;
            while ((str = in.readLine()) != null) {
                process(str, row);
                row++;
            }
            in.close();
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }        
    }

    private void process(String str, int row) {
        
        for (int column=0; column<str.length(); column++){
            char c = str.charAt(column);
            switch (c){
                case ' ':       // empty square
                    _board[row][column] = new SquareFree(row, column);
                    break;
                case '#':       // wall
                    _board[row][column] = new SquareWall(row, column);
                    break;
                case 'F':       // flag
                    SquareFree newSquare = new SquareFree(row, column);
                    _board[row][column] = newSquare;
                    //_board[row][column].setFlag(Game.FLAG_COST);
					Flag newFlag = new Flag(newSquare, Game.FLAG_VALUE);
					try {
						_board[row][column].setFlag(newFlag);
					} catch (CannotFlagSquareException e) {
						e.printStackTrace();
					}
					_flags.addElement(newFlag);
                    break;
                case '.':       // icy patch
                    _board[row][column] = new SquareIce(row, column);
                    break;
            }
        }
    }

    public String getFileName() {
        return _fileName;
    }

    public int getNumberOfRows() {
        return _numberOfRows;
    }

    public int getNumberOfColumns() {
        return _numberOfColumns;
    }
    
    public boolean inSight(Square squareShooter, Square squareVictim) throws MultipleAgentsHaveTheSameLocation {
        boolean ans=true;
        int x1=squareShooter.getRow();
        int y1=squareShooter.getColumn();
        int x2=squareVictim.getRow();
        int y2=squareVictim.getColumn();
        //checking if the row or col are equal, if not returning false.
        if(x1!=x2&&y1!=y2){
            return false;
        }
        // checks if there's an obstacle in shooter's row:
        else if(x1==x2){
            for( int i=Math.min(y1, y2)+1;i<Math.max(y1,y2) && ans; i++){
                if(_board[x1][i] instanceof SquareWall  ||  _board[x1][i].isThereAgentHere()){
                    ans = false;
                }
            }
        }
        // checks if there's an obstacle in shooter's column:
        else if(y1==y2){
            for( int i=Math.min(x1, x2)+1;i<Math.max(x1,x2) && ans;i++){
                if(_board[i][y1] instanceof SquareWall  ||  _board[i][y1].isThereAgentHere()){
                    ans = false;
                }
            }
        }
        return ans;
    }//end of inSight
    
//    @Override
//    public Board clone(){
//        Board ans = null;
//        String          fileNameNew = this._fileName;
//        int             numberOfRowsNew = this._numberOfRows;
//        int             numberOfColumnsNew = this._numberOfColumns;
//        Vector<Flag>    flagsNew = (Vector<Flag>) this._flags.clone();
//        Square[][]      boardNew = new Square[numberOfRowsNew][numberOfColumnsNew];
//        for (int i = 0; i < numberOfRowsNew; i++) {
//            for (int j = 0; j < numberOfColumnsNew; j++) {
//                Square sq = boardNew[i][j];
//                if ( sq instanceof SquareWall ){
////                    boardNew[i][j] = ((SquareWall)_board[i][j])
//                            SquareWall sqw=new SquareWall(j, j);
//                }
//                
//            }
//            Square[] squares = boardNew[i];
//            
//        }
//        
//        return ans;
//    }
    /**
     * 
     * @return the amount of square containing a wall
     */
    public int amountOfWallsInTheBoard(){
    	int counter=0;
    	for (int i = 0; i < _board.length; i++) {
    		for (int j = 0; j < _board[0].length; j++) {
    			if(_board[i][j] instanceof SquareWall){
    				counter++;
    			}
			}
		}
    	return counter;
    }//end of amountOfWallsInTheBoard
    
    @Override
    public boolean equals(Object other) {
    	if (!(other instanceof Board)){
			return false;
		}
    	Board otherBoard = (Board) other;
    	boolean ans = true;
    	for (int i = 0; i < this._numberOfRows && ans; i++) {
    		for (int j = 0; j < this._numberOfColumns && ans; j++) {
    			if (!this._board[i][j].equals(otherBoard.getSquare(i, j))){
    				ans = false;
    			}
			}
		}
    	return ans;
    }
}
